﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace Master
{
    /**
     * Takes care of the traffic lights on an intersection with three trafficlight on each side.
     * 
     * @Creator: V.L. de Jager
     * @Changes made by: 
     *      - A.J. Janssen
     */
    class ThreeLaneTrafficLightStrategy : iTrafficLightStrategy
    {
        /*************
        * VARIABLES *
        *************/
        private Thread strategyThread;
        private TrafficMarshal trafficMarshal = new BasicTrafficMarshal();
        private List<Lane> lanes;
        private TrafficLightDetails trafficLightDetails;

        /********************
         * PUBLIC FUNCTIONS *
         ********************/
        /**
         * gets all the trafficlights fromm all the lanes connected to the intersection and saves the lanes in list of lanes based on orientation.
         * Creates and starts the ThreeLaneTrafficLightStrategy thread.
         */
        public void start(InterSection interSection, TrafficLightDetails trafficLightDetails)
        {
            Dictionary<Side, List<Road>> roadDictionary = interSection.getRoadDictionary();
            lanes = new List<Lane>();
            foreach (List<Road> sublist in roadDictionary.Values)
                foreach (Road road in sublist)
                    foreach (Lane lane in road.getLanes())
                        if (lane.hasTrafficLight())
                            lanes.Add(lane);
            if (interSection.isThreeLaneIntersection())
                trafficMarshal = new ThreeLaneTrafficMarshal();
            this.trafficLightDetails = trafficLightDetails;
            strategyThread = new Thread(handleTraffic);
            strategyThread.Start();
        }
        /**
         * Puts all the trafficlights on red and stops the thread.
         */
        public void stop()
        {
            setStatus(lanes, TrafficLightStatus.RED, 0);
            strategyThread.Abort();
        }

        /**
         * This strategy can only be used on intersections with 4 sides that all have 3 entry-lanes
         * */
        public Boolean canBeUsedOnInterSection(InterSection interSection)
        {
            return (interSection.isThreeLaneIntersection());
        }

        /**
         * Returns the currently used trafficMarshal in use by threeLaneTrafficLightStrategy.         
         */
        public TrafficMarshal getTrafficMarshal()
        {
            return trafficMarshal;
        }

        /*********************
         * PRIVATE FUNCTIONS *
         *********************/
        private Boolean turnLeft = true;
        private Boolean horizontal = false;

        private List<Lane> getCurrentLanes()
        {
            turnLeft = !turnLeft;
            if (!turnLeft)
                horizontal = !horizontal;
            List<Lane> currentLanes = new List<Lane>();
            foreach (Lane lane in lanes)
            {
                int orientation = 0; List<Direction> allowedDirections = null;
                lane.trafficLight.Dispatcher.Invoke(new Action(delegate() { orientation = lane.Orientation; allowedDirections = lane.getAllowedDirections(); }));
                Boolean laneIsHorizontalLane = (orientation == 0 || orientation == 180),
                        laneIsLeftTurn = (allowedDirections.Count == 1 && allowedDirections.Contains(Direction.LEFT)),
                        laneIsRightTurn = (allowedDirections.Count == 1 && allowedDirections.Contains(Direction.RIGHT));
                if (((horizontal && laneIsHorizontalLane) || (!horizontal && !laneIsHorizontalLane)) //check horizontal switch
                    && ((!turnLeft && !laneIsLeftTurn) || (turnLeft && laneIsLeftTurn))) //check turnleft switch
                    currentLanes.Add(lane);
                else if (turnLeft && (horizontal && !laneIsHorizontalLane || !horizontal && laneIsHorizontalLane) && laneIsRightTurn)
                    currentLanes.Add(lane);
            }
            return currentLanes;
        }

        private void setStatus(List<Lane> currentLanes, TrafficLightStatus newStatus, int sleepTime)
        {
            foreach (Lane lane in currentLanes)
                lane.Dispatcher.Invoke(
                        new Action(delegate() { lane.trafficLight.SetValue(TrafficLight.statusProperty, newStatus); })
                        );
            Thread.Sleep(sleepTime);
        }

        private void handleTraffic()
        {
            try
            {
                Thread.Sleep(5000);
                while (true)
                {
                    List<Lane> currentLanes = getCurrentLanes();
                    if (onlyLanesGoingRight(currentLanes))
                        continue;
                    setStatus(currentLanes, TrafficLightStatus.GREEN, trafficLightDetails.getGreenTime());
                    setStatus(currentLanes, TrafficLightStatus.ORANGE, trafficLightDetails.getOrangeTime());
                    setStatus(currentLanes, TrafficLightStatus.RED, trafficLightDetails.getRedTime());
                }
            }
            catch (ThreadAbortException e)
            {
                Trace.WriteLine("Thread was aborted by: " + e.GetType());
                setStatus(lanes, TrafficLightStatus.RED, 0);
                return;
            }
        }

        private Boolean onlyLanesGoingRight(List<Lane> lanes)
        {
            return (Lane.getLanesWithAllowedDirection(lanes, Direction.RIGHT).Count == lanes.Count);
        }
    }
}
